import os
import shutil
import signal
import FileTools
import _const
import config
import time
#
# @author xuyan
#
class apk_workshop:
    _begin_time = 0
    _end_time = 0
    _apk_work_channel_dir = ""
    _apk_work_dir = ""
    _apk_unzip_dir = ""
    _apk_res_guard_compress_dir = ""
    __apk_channel_file = ""
    _and_res_guard_res_config = ""
    _ndk_cpp_path_child = ""
    _apk_signer_jks = ""
    _apk_warehouse = ""

    def __init__(self,apk_path,apk_type,and_res_guard_res_config,apk_signer_jks,apk_warehouse):
        self.apk_path = apk_path
        self.apk_type = apk_type
        self._and_res_guard_res_config = and_res_guard_res_config
        self._apk_signer_jks = apk_signer_jks
        self._apk_warehouse = apk_warehouse
        apk_channel_file_name = os.path.basename(self.apk_path)
        self._apk_channel_file = os.path.basename(self.apk_path)[0:len(apk_channel_file_name) - len(".apk")]
        self._apk_work_channel_dir = os.path.join(_const._resource_path, self._apk_channel_file)
        self._apk_work_dir = os.path.join(self._apk_work_channel_dir, "apk")
        self._apk_unzip_dir = os.path.join(self._apk_work_dir,self._apk_channel_file)
        self._ndk_cpp_path_child = os.path.join(self._apk_work_channel_dir,"cpp")
        self._apk_res_guard_compress_dir = os.path.join(self._apk_work_channel_dir, "compress")

    def start(self):
        try:
            self._begin_time = time.time()
            #解压apk
            self.unZipApk(self.apk_path,self._apk_unzip_dir)
            #打包apk
            zip_apk_file = self._apk_work_dir +"/"+ self._apk_channel_file+".apk"
            self.zipApk(self._apk_unzip_dir,zip_apk_file)
            #资源压缩
            self.compressResource(zip_apk_file,self._apk_res_guard_compress_dir)
            #筛选apk
            select_apk = ""
            for r in os.listdir(self._apk_res_guard_compress_dir):
                if "_signed_aligned" in r:
                    select_apk = self._apk_res_guard_compress_dir +"/"+ r
                    break
            #拷贝cpp
            if not os.path.exists(self._ndk_cpp_path_child):
                FileTools.mkDir(self._ndk_cpp_path_child)
            FileTools.copy_file(_const._ndk_cpp_path, self._ndk_cpp_path_child)
            #计算CRCself._apk_unzip_dir
            self.caculateCRC(select_apk)
            #生成so
            self.compileSo()
            #替换so
            self.replacelibSo()
            #压缩apk
            self.zipApk(self._apk_unzip_dir, zip_apk_file)
            #资源压缩
            self.compressResource(zip_apk_file,self._apk_res_guard_compress_dir)
            #筛选apk
            select_apk = ""
            for r in os.listdir(self._apk_res_guard_compress_dir):
                if "_signed_aligned" in r:
                    select_apk = self._apk_res_guard_compress_dir + "/" + r
                    break
            #内存对齐
            zip_apk_aligned_file = os.path.join(os.path.dirname(zip_apk_file),"app-aligned.apk")
            self.alignApk(select_apk,zip_apk_aligned_file)
            #签名
            out_apk = os.path.join(self._apk_warehouse,os.path.basename(zip_apk_file))
            self.signedApk(zip_apk_aligned_file,out_apk)
            #删除文件夹
            FileTools.delFile(self._apk_work_channel_dir)
        except Exception:
            print(os.path.basename(self.apk_path) + "失败加固")
            print("报错了：",Exception)
        else:
            self._end_time = time.time()
            print(os.path.basename(self.apk_path) + "完成加固,耗时："+self._end_time - self._begin_time+"s")


    # 解压apk
    def unZipApk(self,apk,to_apk_dir):
        print("解压apk")
        command = "java -jar " + _const._apkTool + " d " + apk + " -o " + to_apk_dir
        if(os.system(command) == 0):
            print("apk解压成功！")
        else:
            print("apk解压失败！")
            os.kill(os.getpid(), signal.SIGKILL)

    # 打包apk
    def zipApk(self,from_path,to_apk):
        print("打包apk")
        command = "java -jar " + _const._apkTool+ " b " + from_path + " -o " + to_apk
        if (os.system(command) == 0):
            print("apk打包成功！")
        else:
            print("apk打包失败！")
            os.kill(os.getpid(), signal.SIGKILL)


    # 计算apk的中文件的CRC值
    def caculateCRC(self,apk):
        print("计算apk的CRC值")
        rPathCRC = FileTools.canculateCRC(apk, "r/")
        resourcesCRC = FileTools.canculateCRC(apk, "resources.arsc")
        dexCRC = FileTools.canculateCRC(apk, ".dex")
        sig = ""
        if self.apk_type == 0:
            sig = "CFD5501F61EFBD66770961649972CD806872BABA"
        elif self.apk_type == 1:
            sig = "EDA5BBE128A18AB727437DB2AFB68D82077D42B5"
        FileTools.delFile(self._apk_res_guard_compress_dir)
        # 动态更改appSecurity.cpp文件中的apkResourceCRC、rFileCRC、dexCRC值
        file = self._ndk_cpp_path_child + "/appSecurity.cpp"
        output = []
        with open(file, "+r", encoding="utf-8")as f:
            for line in f.readlines():
                if "*apkResourceCRC" in line:
                    print(line)
                    line = os.path.join('const char *apkResourceCRC = "', resourcesCRC, '";\n')
                    line = line.replace("/", "")
                elif "*rFileCRC" in line:
                    print(line)
                    line = os.path.join('const char *rFileCRC = "', rPathCRC, '";\n')
                    line = line.replace("/", "")
                elif "*dexCRC" in line:
                    print(line)
                    line = os.path.join('const char *dexCRC = "', dexCRC, '";\n')
                    line = line.replace("/", "")
                elif "*apkSignature" in line:
                    print(line)
                    line = os.path.join('const char *apkSignature = "', sig, '";\n')
                    line = line.replace("/", "")
                output.append(line)
            f.close()
        with open(file, "w", encoding="utf-8")as wf:
            wf.writelines(output)
            wf.close()


    # 生成so文件
    def compileSo(self):
        print("生成加固的so文件")
        androidMK = self._ndk_cpp_path_child + "/Android.mk"
        applicationMK = self._ndk_cpp_path_child  + "/Application.mk"
        command = _const._ndk_path + " NDK_PROJECT_PATH=" +  self._ndk_cpp_path_child  + " APP_BUILD_SCRIPT=" + androidMK + " NDK_APPLICATION_MK=" + applicationMK
        print(command)
        if (os.system(command) == 0):
            # FileTools.delFile(self._apk_unzip_dir)
            print("生成so成功！")
        else:
            print("生成so失败！")
            os.kill(os.getpid(), signal.SIGKILL)

    # 压缩资源文件
    # 去除7zip，节省时间
    def compressResource(self,apk,toPath):
        print("压缩资源文件")
        # command = "java -jar " + _const._and_res_guard_file \
        #                + " " + apk + " -config " + self._and_res_guard_res_config + " -out " + toPath \
        #                + " -7zip " + _const._zip7za_path + " -zipalign " + _const._zipalign_path

        command = "java -jar " + _const._and_res_guard_file \
                  + " " + apk + " -config " + self._and_res_guard_res_config + " -out " + toPath \
                  + " -zipalign " + _const._zipalign_path

        if (os.system(command) == 0):
            FileTools.delFile(apk)
            print("压缩资源文件成功！")
        else:
            print("压缩资源文件失败！")
            os.kill(os.getpid(), signal.SIGKILL)
        pass

    #替换so
    def replacelibSo(self):
        print("替换so")
        ndk_cpp_libs_path = os.path.join(self._ndk_cpp_path_child, "libs")
        for r in os.listdir(ndk_cpp_libs_path):
            lib_path = ndk_cpp_libs_path + "/" + r + "/"
            for f_dir in os.listdir(lib_path):
                os_path = lib_path + f_dir
                lib_os_path = self._apk_unzip_dir + "/lib/" + r + "/" + f_dir
                if FileTools.dirIsExists(lib_os_path):  # 只拷贝apk中存在的so
                    shutil.copyfile(os_path, lib_os_path)
        print("替换成功")

    #字节对齐
    def alignApk(self,apk,out_apk):
        print("对apk进行字节对齐")
        command = _const._zipalign_path + " -v -f 4 " + apk + " " + out_apk
        if (os.system(command) == 0):
            # FileTools.delFile(apk)
            print("对apk进行字节对齐成功！")
        else:
            print("对apk进行字节对齐失败！")
            os.kill(os.getpid(), signal.SIGKILL)


    # 签名
    def signedApk(self,apk,out_apk):
        print("对apk签名")
        out_apk_path = os.path.dirname(out_apk)
        if not FileTools.dirIsExists(out_apk_path):
            FileTools.mkDir(out_apk_path)
        command = _const._apk_signer + " sign --ks " + self._apk_signer_jks \
                       + " --ks-key-alias eng100 --ks-pass pass:wanhe100 --key-pass pass:wanhe100 --out " + out_apk + " " + apk
        print(command)
        if (os.system(command) == 0):
            FileTools.delFile(apk)
            print("对apk进行签名成功！")
        else:
            print("对apk进行签名失败！")
            os.kill(os.getpid(), signal.SIGKILL)
